/**	
 * タイトル：	信息读取类
 * 説明：    	
 *			
 * 会社名：	紹興日創軟件有限公司
 * 変更履歴：	2013/12/30 NIS 何
 *			新規作成
 *
 */
package com.nissoft.framework.message;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.nissoft.framework.util.ClassUtil;

/**
 * @author NIS 何
 * @version 1.0
 */
public abstract class FileMessageReader<E> implements MessageReader {

	public static final int DEFAULT_CACHE_LIMIT = 1024;
	public static final String DEFAULT_SEPARATOR = ".";
	public static final String DEFAULT_ENCODING = "utf-8";

	protected volatile int cacheLimit = DEFAULT_CACHE_LIMIT;

	protected String separator = DEFAULT_SEPARATOR;

	protected String encoding = DEFAULT_ENCODING;

	protected List<String> extensions;

	protected String prefixPath;

	protected final Map<String, Long> fileCache = new ConcurrentHashMap<String, Long>(
			DEFAULT_CACHE_LIMIT);

	@SuppressWarnings("serial")
	protected final Map<String, E> messageCache = new LinkedHashMap<String, E>(
			DEFAULT_CACHE_LIMIT, 0.75f, true) {
		@Override
		protected boolean removeEldestEntry(Map.Entry<String, E> eldest) {
			if (size() > getCacheLimit()) {
				fileCache.remove(eldest.getKey());
				return true;
			} else {
				return false;
			}
		}
	};

	public FileMessageReader() {
		extensions = Arrays.asList(getDefaultExtensions());
	}

	/**
	 * 默认后缀名
	 * 
	 */
	protected abstract String[] getDefaultExtensions();

	/**
	 * 获取文件主键
	 * 
	 * @param pattern
	 *            信息规则
	 */
	protected abstract String getFileKey(String pattern);

	/**
	 * 读取文件封装数据格式
	 * 
	 * @param file
	 */
	public abstract E loadFile(File file) throws IOException;

	/**
	 * 内部读取
	 * 
	 * @param pattern
	 * @param e
	 */
	public abstract String readInternal(String pattern, E e);

	public String read(String pattern) throws IOException {

		// 处理
		pattern = pattern.trim();
		// 是否符合规则要求
		if (!checkPattern(pattern))
			return pattern;

		// 文件主键
		String fileKey = getFileKey(pattern);
		// 获取文件
		File file = getFile(fileKey);
		// 判断是否缓存
		if (!isCache()) {
			return readInternal(pattern, loadFile(file));
		}
		// 缓存中的文件修改时间
		Long cacheModifiedTime = fileCache.get(fileKey);
		// 文件最后修改时间
		Long lastModifiedTime = file.lastModified();
		E e = null;
		// 判断是否需要重新载入文件
		if (cacheModifiedTime == null || cacheModifiedTime < lastModifiedTime) {
			// 载入文件
			e = loadFile(file);
			synchronized (messageCache) {
				fileCache.put(fileKey, lastModifiedTime);
				messageCache.put(fileKey, e);
			}
		} else {
			e = messageCache.get(fileKey);
		}
		// 内部读取
		return readInternal(pattern, e);
	}

	/**
	 * 判断规则是否符合要求
	 */
	protected boolean checkPattern(String pattern) {
		return pattern.indexOf(" ") == -1;
	}

	/**
	 * 根据主键取得文件
	 * 
	 * @param 文件主键
	 * @return 文件
	 * @throws FileNotFoundException
	 */
	protected File getFile(String fileKey) throws FileNotFoundException {

		StringBuffer sb = new StringBuffer(prefixPath);
		sb.append(fileKey.replace(separator, File.separator));
		String path = sb.toString();
		for (String ext : extensions) {
			File file = new File(path + ext);
			if (file.exists()) {
				return file;
			}
		}
		throw new FileNotFoundException(path + extensions);
	}

	public boolean isCache() {
		return (this.cacheLimit > 0);
	}

	public int getCacheLimit() {
		return cacheLimit;
	}

	public void setCacheLimit(int cacheLimit) {
		this.cacheLimit = cacheLimit > 0 ? cacheLimit : 0;
	}

	public String getSeparator() {
		return separator;
	}

	public void setSeparator(String separator) {
		this.separator = separator;
	}

	public String getEncoding() {
		return encoding;
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	public List<String> getExtensions() {
		return extensions;
	}

	public void setExtensions(List<String> extensions) {
		this.extensions = new ArrayList<String>();
		for (String ext : extensions) {
			this.extensions.add(ext.trim());
		}
	}

	public String getPrefixPath() {
		return prefixPath;
	}

	public void setPrefixPath(String prefixPath) {

		this.prefixPath = ClassUtil.getPath(prefixPath);
	}

}
